﻿using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using DeepZoomPivotConstructor.VisualLib;
using RenderEngineInterfaces;

namespace SimpleFileRendering
{
    /// <summary>
    /// Renders a string with a wild-card in it to a series of FileInfo's.
    /// </summary>
    [Export(typeof(IRenderItem))]
    [RenderInfo(typeof(string))]
    public class StringWildCardToInfoRender : IRenderItem
    {
        /// <summary>
        /// Contains the relative path base - which we can use if we know about it.
        /// </summary>
#pragma warning disable 0649
        [Import("RelativePathBase", AllowDefault = true)]
        private DirectoryInfo RelativePathBase;
#pragma warning restore 0649

        /// <summary>
        /// Holds the render engine
        /// </summary>
#pragma warning disable 0649
        [Import]
        private IRenderEngine MyRender;
#pragma warning restore 0649

        /// <summary>
        /// Holds onto our 2x3 layout manager!
        /// </summary>
#pragma warning disable 0649
        [Import]
        private ILayoutHelper MyLayout;
#pragma warning restore 0649

        /// <summary>
        /// Render the object that points to a file info or a string of directories, etc.
        /// </summary>
        /// <param name="what"></param>
        /// <returns></returns>
        public Task<Visual> Render(object what)
        {
            // Do it as files. Since we have some work to do here, we need to spin this off
            // to another thread.

            return Task<Visual>.Factory.StartNew(() =>
            {
                var renderedFiles = (from f in ParseAsFiles(what as string)
                                     let r = MyRender.RenderToFuture(f.FullName)
                                     where r != null
                                     select r).ToArray();
                Task<Visual>.WaitAll(renderedFiles);
                var results = renderedFiles.Where(r => r.Result != null).Select(r => r.Result).ToArray();
                var totalVisual = MyLayout.LayoutIn2By3Rows(results);
                return totalVisual;
            });
        }

        /// <summary>
        /// Figure out if we can render this guy. It needs to be around and something we know how to deal with!
        /// </summary>
        /// <param name="what"></param>
        /// <returns></returns>
        public RenderPluginPriority RenderPriority(object what)
        {
            try
            {
                //
                // Make sure we are dealing with a wild-card. Other translators can handle single
                // nice file names as strings. :-)
                //

                if (HasWildcard(what as string))
                {
                    if (ParseAsFiles(what as string).Any())
                        return RenderPluginPriority.RPPGeneric;
                }
                return RenderPluginPriority.RPPCantDoIt;
            }
            catch
            {
                return RenderPluginPriority.RPPCantDoIt;
            }
        }

        /// <summary>
        /// Try to parse the current guy as a set of files.
        /// </summary>
        /// <param name="p"></param>
        /// <returns></returns>
        private IEnumerable<FileInfo> ParseAsFiles(string p)
        {
            string path = p;
            if (IsRelative(p))
            {
                if (RelativePathBase == null)
                    yield break;

                path = string.Format(@"{0}\{1}", RelativePathBase.FullName, p);
            }

            // Do the wild-card expansion.
            var dirPart = Path.GetDirectoryName(path);
            if (dirPart.Length == 0)
                dirPart = ".";
            var filePart = Path.GetFileName(path);

            foreach (var fielpath in Directory.GetFiles(dirPart, filePart))
            {
                yield return new FileInfo(fielpath);
            }

        }

        /// <summary>
        /// Try to parse as a directory
        /// </summary>
        /// <param name="dirPath"></param>
        /// <returns></returns>
        private DirectoryInfo ParseAsSingleDirectory(string dirPath)
        {
            if (HasWildcard(dirPath))
                return null;

            // See if it is relative
            if (IsRelative(dirPath))
            {
                if (RelativePathBase == null)
                    return null;

                var d = new DirectoryInfo(string.Format(@"{0}\{1}", RelativePathBase.FullName, dirPath));
                if (d.Exists)
                    return d;
                return null;
            }

            // In some sense, this is simpler if it isn't relative!
            var dir = new DirectoryInfo(dirPath);
            if (dir.Exists)
                return dir;
            return null;
        }

        /// <summary>
        /// If there is a wildcard character in there, then return true.
        /// </summary>
        /// <param name="dirPath"></param>
        /// <returns></returns>
        private bool HasWildcard(string dirPath)
        {
            if (dirPath.Contains("*"))
                return true;
            return false;
        }

        /// <summary>
        /// See if the path given is relative or not.
        /// </summary>
        /// <param name="p"></param>
        /// <returns></returns>
        private bool IsRelative(string p)
        {
            if (p.StartsWith("\\"))
                return false;

            if (p.Contains(":"))
                return false;

            return true;
        }
    }
}
